#pragma once
#include"MY_API.hpp"
#include<unordered_map>
#include<functional>
#include<mutex>
#include<websocketpp/server.hpp>
#include<websocketpp/config/asio_no_tls.hpp>

typedef websocketpp::server<websocketpp::config::asio> web_server_t;
enum users_stat{UNLOGIN, LOGIN};

class session{
private:
    uint64_t _ssid;//用户对应的sessionID
    int _uid;//用户ID
    int _status;//用户当前所处的状态
    web_server_t::timer_ptr _tp; //session销毁定时器
public:

    session(uint64_t ssid):_ssid(ssid){ log_message(NORMAL, "session %p 创建成功", this);}
    ~session(){ log_message(NORMAL, "session %p 被释放", this);}
    void set_user(int uid){ _uid = uid;}
    uint64_t get_ssid(){ return _ssid;}
    int get_user(){ return _uid;}
    void set_statu(int statu) { _status = statu;}
    bool is_login(){ return _status;}
    void set_timer(const web_server_t::timer_ptr& tp){ _tp = tp;}
    web_server_t::timer_ptr get_timer(){ return _tp;}
};


#define SESSION_TIMEOUT 1200000
#define SESSION_FOREVER -1
#define RECONNECTION 5000
#define MATCH_TIME (rand()%4 + 2) * 1000


using session_ptr = std::shared_ptr<session>;
class session_manage{
private:
    uint64_t _next_ssid;
    std::mutex _mutex;
    std::unordered_map<uint64_t, session_ptr> _sessions;
    web_server_t *_server;
public:
    session_manage(web_server_t *server):_next_ssid(1), _server(server){}

    session_ptr create_session(uint64_t uid, int status)
    {    
        std::unique_lock<std::mutex> lock(_mutex);
        auto pos = _sessions.find(uid);
        if (pos == _sessions.end())
        {
            session_ptr sp(new session(_next_ssid));
            sp->set_user(uid);
            sp->set_statu(status);
            _sessions.insert(std::make_pair(_next_ssid, sp));
            _next_ssid++;
            return sp;
        }
        
        session_ptr sp = pos->second;
        return sp;
    }

    session_ptr get_session_by_ssid(uint64_t ssid) 
    {
        std::unique_lock<std::mutex> lock(_mutex);  
        auto pos = _sessions.find(ssid);
        if (pos == _sessions.end())
        {
            return session_ptr();
        }
        return pos->second;
    }

    void append_session(session_ptr &ssp)
    {
        std::unique_lock<std::mutex> lock(_mutex);  
        _sessions.insert(std::make_pair(ssp->get_ssid(), ssp));
    }

    void set_session_expire_time(uint64_t ssid, int ms)
    {
        //依赖于websocketpp的定时器来完成session生命周期的管理。
        // 登录之后，创建session，session需要在指定时间无通信后删除
        // 但是进入游戏大厅，或者游戏房间，这个session就应该永久存在
        // 等到退出游戏大厅，或者游戏房间，这个session应该被重新设置为临时，在长时间无通信后被删除
        session_ptr ssp = get_session_by_ssid(ssid);
        if (ssp.get() == nullptr)
        {
            return;
        }
        web_server_t::timer_ptr tp = ssp->get_timer();

        // 1. 在session永久存在的情况下，设置永久存在，不需要操作
        if (tp == nullptr && ms == SESSION_FOREVER)
            return;
        // 2. 在session永久存在的情况下，设置指定时间之后被删除的定时任务
        else if (tp == nullptr && ms != SESSION_FOREVER)
        {   
            //创建一个临时的定时任务
            web_server_t::timer_ptr tmp = _server->set_timer(SESSION_TIMEOUT, \
            std::bind(&session_manage::destory_session, this, ssp->get_ssid()));
            ssp->set_timer(tmp);
            return;        
        }
        //3.在session开始计时的情况下，将session设置为永久模式
        else if (tp != nullptr && ms == SESSION_FOREVER)
        {
            //先取消这个计时任务，取消是不意味着不执行，而是开始执行，会删掉原有session信息
            tp->cancel();//注意这里取消不是立即取消，而是服务器察觉后才开始取消
            //因此不能立即给session添加新的ssid, 否则会导致被删掉
            ssp->set_timer(web_server_t::timer_ptr());
            _server->set_timer(0, std::bind(&session_manage::append_session, this, ssp));      
        }
        //4.在session开始计时的情况下，更新session的计时时间
        else if (tp != nullptr && ms != SESSION_FOREVER)
        {
            //和之前步骤类似，先取消
             tp->cancel();

            //先将定时任务置为空
            ssp->set_timer(web_server_t::timer_ptr());
            _server->set_timer(0, std::bind(&session_manage::append_session, this, ssp));

            //创建一个临时的定时任务，并设置进去
            web_server_t::timer_ptr tmp = _server->set_timer(ms, \
            std::bind(&session_manage::destory_session, this, ssp->get_ssid()));
            ssp->set_timer(tmp);
        }
    }

    void destory_session(uint64_t ssid)
    {
        std::unique_lock<std::mutex> lock(_mutex);  
        _sessions.erase(ssid);
    }
};